#include <iostream>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

#define MAX 1024

using namespace std;

// 管道的4种情况
// 1.正常情况，如果管道没有数据了，读端必须等待，直到有数据为止(写端写入数据)
// 2.正常情况，如果管道被写满了，写端必须等待，直到有空间为正(读端读走数据)
// 3.写端关闭，读端一直读取，读端会读到read返回值为0，表示读到文件结尾
// 4.读端关闭，写端一直写入，0S会直接杀掉写端进程，通过想目标进程发送SIGPIPE(13)信号，终止目标进程

// 管道的5种特性
// 1.匿名管道,可以允许具有血缘关系的进程之间进行进程间通信，常用与父子,仅限于此
// 2.匿名管道，默认给读写端要提供同步机制---了解现象就行
// 3.面向字节流的入--了解现象就行
// 4.管道的生命周期是随进程的
// 5.管道是单向通信的，半双工通信的一种特殊情况,

int main()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    // 定义 n
    (void)n;
    cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }

    // 子写，父读，
    // 3. 关闭父子不需要的fd，形成单向通信的管道
    if(id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 写入
        int cnt = 0;
        while(true)
        {
            // char c = 'a';
            // write(pipefd[1], &c, 1);
            // cnt++;
            char message[MAX];
            snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
            cnt++;
            write(pipefd[1], message, strlen(message));
            //sleep(2);
            cout << "writing cnt: " << cnt << endl;
            // if(cnt == 2)
            // {
            //      close(pipefd[1]);
            //      break;
            // }
        }

        exit(0);
    }

    // 父进程
    close(pipefd[1]);

    // 读取
    char buffer[MAX];
    while(true)
    {
        //sleep(4);
        ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
        if(n == 0)
        {
             cout << "child qiut, read tail" << endl;
             break;
         }
        else if(n > 0)
        {
            buffer[n] = 0; // '\0', 当作字符串
            cout << getpid() << ": " << "child say: " << buffer << " to me!" << endl;
        }
        cout << "father return val(n)" << n << endl;
        sleep(1);
        
        break;
    }

    cout << "close point read" << endl;
    close(pipefd[0]);

    sleep(3);

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid == id)
    {
        cout << "wait seccess, exit sig: " << (status&0x7f) << endl;
    }

    return 0;
}